
enum state {
    PEDDING = 1,
    FULFILLED,
    REJECTED
};

type cb = (v) => void;
type fn = (resolve: cb, reject?: cb) => void;

export default class MyPromise{
    state = state.PEDDING;
    _value;
    set value(v){
        this._value = v;
        for(let fn of this.fulfilledFn){
            this._value = fn(this._value)
        }
    }

    get value(){
        return this._value;
    }

    _err;
    set err(err){
        this._err = err;
        this.rejectedFn[0]  && this.rejectedFn[0](err);
        this.catchFn[0] && this.catchFn[0](err);
    }
    get err(){
        return this._err;
    }

    fulfilledFn: cb[] = [];

    rejectedFn: cb[] = [];
    catchFn: cb[] = [];
    

    constructor(fn: fn){
        fn(this.resolve.bind(this), this.reject.bind(this));
    }

    
    resolve(v?){
        if(this.state == state.PEDDING){
            this.state = state.FULFILLED;
            this.value = v;
            return this.value;
        }
    }


    reject(err){
        if(this.state == state.PEDDING){
            this.state = state.REJECTED;
            this.err = err;
            return this.err;
        }
    }


    then(resolve: cb, reject = v => v){
        this.fulfilledFn.push(resolve);
        this.rejectedFn.push(reject);
        if(this.state == state.FULFILLED){
            resolve(this.value);
        }
        if(this.state == state.REJECTED){
            reject(this.err);
        }
        return this;
    }

    catch(fn: cb){
        this.catchFn.push(fn);
        if(this.state == state.REJECTED){
            fn(this.err);
        }
    }


    static all(arr: MyPromise[]){
        return new MyPromise((resolve, reject) => {
            const l = arr.length;
            let i = 0;
            const value = [];
            function run(arr: MyPromise[]){
                if(i == l){
                    resolve(value);
                    return;
                }
                const item = arr[i];
                item.then((v) => {
                    value.push(v);
                    i++;
                    run(arr);
                }, (err) => {
                    reject(err);
                });
                
            }
            run(arr);
        });

    }
}